
//Namespace
package com.katujo.squirrel;

//Java imports
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;

/**
 * Adds transactional support to squirrel.
 * @author Johan Hertz
 *
 */
public class SquirrelTransactionManager extends SquirrelManager
{
	//Fields
	private ThreadLocal<Connection> transaction = new ThreadLocal<Connection>();
	
	/**
	 * Create the Object. 
	 *
	 */
	public SquirrelTransactionManager()
	{		
		super();
	}	
		
	/**
	 * Create the Object with the data source to use.
	 * @param source
	 */
	public SquirrelTransactionManager(DataSource source)
	{	
		super(source);
	}	
	
	/**
	 * Start a transaction.
	 * @throws SquirrelException
	 */
	public void start() throws SquirrelException
	{
		//Fields
		Connection connection = null;
		
		//Try to start transaction
		try
		{
			//Check if a transaction has already been started
			if(transaction.get() != null)
				throw new SquirrelException("Transaction already started");
			
			//Get a connection 
			connection = getConnection();
			
			//Set auto commit
			connection.setAutoCommit(false);
			
			//Add the connection to the transaction
			transaction.set(connection);			
		}
		
		//Failed
		catch(Exception ex)
		{
			throw new SquirrelException("Failed to start transaction", ex);
		}	
		
		//Clean up
		finally
		{
			//Close the connection if it were not set to the transaction
			try 
			{
				if(transaction.get() == null) 
					connection.close();
			}
			catch(Throwable t) {}			
		}
	}
	
	/**
	 * Rollback and then close the current transaction.
	 * <p>
	 * This will always try to close the connection even if
	 * the rollback fails;
	 * </p>
	 * @throws SquirrelException
	 */
	public void rollback() throws SquirrelException
	{
		//Try to start transaction
		try
		{
			//Check if a transaction has been started
			if(transaction.get() == null)
				throw new SquirrelException("No transaction started");
			
			//Rollback
			transaction.get().rollback();
		}
		
		//Failed
		catch(Exception ex)
		{
			throw new SquirrelException("Failed to rollback transaction", ex);
		}	
		
		//Clean up
		finally
		{
			//Close connection
			try{transaction.get().close();} catch(Throwable t){}
			
			//Clear the connection from the transaction
			try{transaction.set(null);} catch(Throwable t){}
		}
	}
	
	/**
	 * Commit and then close the current transaction.
	 * <p>
	 * This will always try to close the connection even if
	 * the commit fails;
	 * </p>
	 * @throws SquirrelException
	 */
	public void commit() throws SquirrelException
	{
		//Try to commit the transaction
		try
		{
			//Check if a transaction has been started
			if(transaction.get() == null)
				throw new SquirrelException("No transaction started");
						
			//Commit
			transaction.get().commit();			
		}
		
		//Failed
		catch(Exception ex)
		{
			throw new SquirrelException("Failed to commit transaction", ex);
		}
		
		//Clean up
		finally
		{
			//Close connection
			try {transaction.get().close();} catch(Throwable t) {}
			
			//Clear the connection from the transaction
			try{transaction.set(null);} catch(Throwable t){}
		}
		
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#delete(java.util.Collection)
	 */
	@Override
	public <T> void delete(Collection<T> items) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.delete(transaction.get(), items);
		
		//Not in transaction
		else super.delete(items);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#delete(java.lang.Object)
	 */
	@Override
	public <T> void delete(T item) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.delete(transaction.get(), item);
		
		//Not in transaction
		else super.delete(item);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#deleteLink(java.lang.Object)
	 */
	@Override
	public <T> void deleteLinks(T item) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.deleteLinks(transaction.get(), item);
		
		//Not in transaction
		else super.deleteLinks(item);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#execute(java.lang.String)
	 */
	@Override
	public void execute(String sql) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.execute(transaction.get(), sql);
		
		//Not in transaction
		else super.execute(sql);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#insert(java.util.Collection)
	 */
	@Override
	public <T> void insert(Collection<T> items) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.insert(transaction.get(), items);
		
		//Not in transaction
		else super.insert(items);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#insert(java.lang.Object)
	 */
	@Override
	public <T> void insert(T item) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.insert(transaction.get(), item);
		
		//Not in transaction
		else super.insert(item);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#insertLinks(java.lang.Object)
	 */
	@Override
	public <T> void insertLinks(T item) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.insertLinks(transaction.get(), item);
		
		//Not in transaction
		else super.insertLinks(item);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#select(java.lang.Class, java.util.Map, java.lang.String)
	 */
	@Override
	public <T> List<T> select(Class<?> clazz, Map<String, Object> parameters, String order) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			return super.select(transaction.get(), clazz, parameters, order);
				
		//No in transaction
		return super.select(clazz, parameters, order);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#select(java.lang.Class, java.util.Map)
	 */
	@Override
	public <T> List<T> select(Class<?> clazz, Map<String, Object> parameters) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			return super.select(transaction.get(), clazz, parameters);
				
		//No in transaction
		return super.select(clazz, parameters);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#select(java.lang.Class, java.lang.Object[])
	 */
	@SuppressWarnings("unchecked")
	@Override
	public <T> T select(Class<?> clazz, Object ... keys) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)			
			return (T) super.select(transaction.get(), clazz, keys);
						
		//Not in transaction
		return (T) super.select(clazz, keys);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#select(java.lang.Class, java.sql.PreparedStatement)
	 */
	@Override
	public <T> List<T> select(Class<?> clazz, PreparedStatement statement) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			return super.select(transaction.get(), clazz, statement);
				
		//No in transaction
		return super.select(clazz, statement);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#select(java.lang.Class, java.lang.String)
	 */
	@Override
	public <T> List<T> select(Class<?> clazz, String sql) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			return super.select(transaction.get(), clazz, sql);
				
		//No in transaction
		return super.select(clazz, sql);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#select(java.lang.Object)
	 */
	@Override
	public <T> T select(T like) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			return super.select(transaction.get(), like);
				
		//No in transaction
		return super.select(like);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#selectAll(java.lang.Class, java.lang.String)
	 */
	@Override
	public <T> List<T> selectAll(Class<?> clazz, String order) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			return super.selectAll(transaction.get(), clazz, order);
				
		//No in transaction
		return super.selectAll(clazz, order);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#selectAll(java.lang.Class)
	 */
	@Override
	public <T> List<T> selectAll(Class<?> clazz) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			return super.selectAll(transaction.get(), clazz);
				
		//No in transaction
		return super.selectAll(clazz);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#selectLink(java.lang.Object)
	 */
	@Override
	public <T> void selectLink(T item) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.selectLink(transaction.get(), item);
				
		//Not in transaction
		else super.selectLink(item);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#update(java.util.Collection)
	 */
	@Override
	public <T> void update(Collection<T> items) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.update(transaction.get(), items);
				
		//No in transaction
		else super.update(items);
	}

	/* (non-Javadoc)
	 * @see com.katujo.squirrel.SquirrelManager#update(java.lang.Object)
	 */
	@Override
	public <T> void update(T item) throws SquirrelException
	{
		//In transaction
		if(transaction.get() != null)
			super.update(transaction.get(), item);
				
		//Not in transaction
		else super.update(item);
	}			
	
}
